package com.sy.controller;

import com.alibaba.fastjson.JSON;
import com.sy.bean.JwtOperation;
import com.sy.dto.UserDto;
import com.sy.dto.UserInfo;
import com.sy.dto.userRoleDto;
import com.sy.entity.User;
import com.sy.entity.UserPermit;
import com.sy.entity.UserRole;
import com.sy.exception.ParamException;
import com.sy.exception.QuestionException;
import com.sy.constant.Method;
import com.sy.dto.JsonResult;
import com.sy.repository.UserPermitRepository;
import com.sy.service.UserRoleService;
import com.sy.service.UserService;
import com.sy.utils.HttpUtils;
import com.sy.utils.QaSystemUtils;
import com.sy.vo.UserVo;
import com.zbb.template.MyRedisTemplate;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

import static com.sy.constant.BusinessConstant.*;

/**
 * @author zbbstart
 * @date 2021/1/23 - 12:05
 */
@RestController
@RequestMapping("users")
@Api(tags = "用户操作相关的接口")
@Slf4j
public class UserController {
    @Autowired
    private MyRedisTemplate myRedisTemplate;
    @Autowired
    private UserService userService;
    @Autowired
    private UserPermitRepository userPermitRepository;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private HttpUtils httpUtils;
    @Autowired
    private AuthenticationConfiguration authenticationConfiguration;
    @Autowired
    private JwtOperation jwtOperation;
    @Autowired
    private PasswordEncoder encoder;
    @Autowired
    @Qualifier("jdbcUseService")
    private UserDetailsService userDetailsService;

    @PostMapping("/regist")
    @ApiOperation(value = "注册用户", notes = "根据前端传来的用户信息注册到用户表中")
    public JsonResult regist(@ApiParam(name = "userVo", value = "前端传递的用户信息封装出来的对象", required = true) @RequestBody UserVo userVo) {
        JsonResult result = null;
        try {
            userService.insert(userVo);
            result = httpUtils.buildSuccess(Method.POST, "注册成功！");
        } catch (QuestionException qe) {
            result = httpUtils.buildFailure(qe.getMessage(), Method.POST);
        } catch (Exception e) {
            e.printStackTrace();
            result = httpUtils.build500();
        }
        return result;
    }


    /**
     * 登录操作
     *
     * @param userVo getUserName登录使用的用户名
     * @param userVo getPassword登录使用的密码（注意：登录使用的是明文密码，数据库保存的是BCryptPasswordEncoder加密的密码）
     * @return
     * @throws Exception
     */
    @PostMapping("login")
    public JsonResult login(@RequestBody UserVo userVo, HttpServletResponse res) {
        JsonResult result = new JsonResult();
        try {
            String name = userVo.getUserName();
            String pwd = userVo.getPassword();
            User user1 = userService.getUserInfoByUserName(name);
            if (null == user1) {
                result = httpUtils.buildFailure("用户不存在，请您先去注册!", Method.GET);
            } else {
                //登录超过3次的冻结处理
                if (myRedisTemplate.exists(LOGIN_COUNT) && myRedisTemplate.get(LOGIN_COUNT).equals(LOGIN_MAX_COUNT)) {
                    throw new BadCredentialsException(ERROR_INFO);
                }
                //获取认证管理器
                AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
                Authentication authentication = authenticationManager.authenticate(
                        new UsernamePasswordAuthenticationToken(name, pwd));
                //存储认证信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
                //获取认证通过的具体用户信息
                UserDetails userDetails = userDetailsService.loadUserByUsername(name);
                List<String> roles = userDetails.getAuthorities().stream().map(
                        e -> ((GrantedAuthority) e).getAuthority()).collect(Collectors.toList());
                //构建的用户对象
                UserDto user = new UserDto();
                user.setUserId(String.valueOf(user1.getId()));
                user.setUserName(userDetails.getUsername());
                user.setUserPwd(userDetails.getPassword());
                user.setRoles(roles);
                //创建JWT
                String token = jwtOperation.createJwt(user);
                result.setCode(200);
                //登录成功返回用户基本信息以及Token
                Map<String, Object> data = new LinkedHashMap<String, Object>();
                data.put("token", token);
                data.put("roles", roles);
                // 这一步很关键，首先让JWT存住密码，其次返回数据的时候置空密码，确保安全性
                user.setUserPwd(null);
                result.setData(data);
            }

        } catch (QuestionException qe) {
            qe.printStackTrace();
            result = httpUtils.buildFailure(qe.getMessage(), Method.GET);
        } catch (BadCredentialsException e) {
            e.printStackTrace();
            //登录超三次冻结
            if (myRedisTemplate.exists(LOGIN_COUNT)) {
                String count = myRedisTemplate.get(LOGIN_COUNT);
                if (count.equals(LOGIN_MAX_COUNT)) {
                    result = httpUtils.build403(ERROR_INFO);
                } else {
                    myRedisTemplate.incr(LOGIN_COUNT);
                    result = httpUtils.build403(e.getMessage());
                }
            } else {
                myRedisTemplate.incr(LOGIN_COUNT);
                myRedisTemplate.expire(LOGIN_COUNT, 30 * 60);
                result = httpUtils.build403(e.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
            result = httpUtils.build500();
        }
        //设置状态码
        res.setStatus(result.getCode());
        return result;
    }

    @GetMapping("/info")
    @ApiOperation(value = "获取用户信息", notes = "根据是否存在cookie获取用户信息")
    public JsonResult getLoginUserInfo(@RequestHeader("auth-token") String token, HttpServletResponse res) {
        JsonResult result = new JsonResult();
        //头信息中没有token
        if (StringUtils.isBlank(token)) {
            throw new ParamException("请先进行登录");
        }

        //获取Claims
        Claims claims = jwtOperation.parseJwt(token);
        //根据token无法得到claims
        if (null == claims) {
            throw new ParamException("请先进行登录");
        }
        //获取用户信息
        UserDto user = JSON.parseObject(String.valueOf(claims.get("user")), UserDto.class);
        System.out.println("user:" + user);
        //登录成功前删除存在Redis数据库中的登录次数的key
        myRedisTemplate.del(LOGIN_COUNT);
        result = httpUtils.buildSuccess(Method.GET, user);
        res.setStatus(result.getCode());
        return result;
    }

    /**
     * 获取所有的用户信息列表
     *
     * @return 用户信息列表
     */
    @GetMapping
    public JsonResult getAllUsersInfo() {
        JsonResult result = new JsonResult();
        List<User> users = userService.getAllUsersInfo();
        System.out.println("users:" + Arrays.toString(users.toArray()));
        List<UserInfo> userInfos = new ArrayList<>();
        for (User user : users) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUserNo(user.getId());
            userInfo.setUserName(user.getUsername());
            UserPermit userPermit = userPermitRepository.findAllByUserId(user.getId());
            userInfo.setRolesDesc(QaSystemUtils.getUserPermits(userPermit));
            userInfos.add(userInfo);
        }
        result = httpUtils.buildSuccess(Method.GET, userInfos);
        result.setCount(users.size());
        return result;
    }

    /**
     * 新增用户
     */
    @PostMapping
    public JsonResult createUser(@RequestBody UserVo userVo) {
        JsonResult result = new JsonResult();
        User user = new User();
        user.setUsername(userVo.getUserName());
        // 设置默认密码
        user.setPassword(DEFAULT_PASSWORD);
        // 设置默认的用户状态
        user.setStatus(DEFAULT_STATUS);
        // 设置默认用户
        user.setDescn(DEFAULT_STATUS_NAME);
        //  添加用户
        User insert = userService.insert(user);
        //  添加用户后返回的用户主键值
        Integer userId = insert.getId();
        // 用户角色封装
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(DEFAULT_USER_ROLE_ID);
        //  添加用户的角色序列（2）
        userRoleService.insertUserRole(userRole);
        //  添加用户的功能权限
        List<Integer> roleIds = userVo.getRoleIds();
        UserPermit userPermit = QaSystemUtils.getUserPermit(roleIds, userId);
        UserPermit save = userPermitRepository.save(userPermit);
        if (null != save) {
            result = httpUtils.buildSuccess(Method.POST, "添加成功");
        }
        return result;
    }

    /**
     * 删除用户
     * 可以是单个用户的删除，也可以是多个用户的删除
     */
    @DeleteMapping
    public JsonResult deleteUser(@RequestParam("userNos") List<Integer> userNos) {
        JsonResult result = new JsonResult();
        log.info("删除用户的编号:" + Arrays.toString(userNos.toArray()));
        try {
            for (Integer userId : userNos) {
                userPermitRepository.deleteById(userId);
                userRoleService.deleteUserRole(userId);
                userService.delete(userId);
            }
        } catch (Exception e) {
            e.printStackTrace();
            result = httpUtils.build500();
        }
        return result;
    }

    /**
     * 用户授权时加载的用户权限
     */
    @GetMapping({"userNo"})
    public JsonResult loadUserPermit(@PathVariable("userNo") Integer userNo){
        JsonResult result = new JsonResult();
        try {
            log.info("用户授权时加载的用户权限:"+userNo);
            User user = userService.findUserByUserId(userNo);
            UserPermit userPermit = userPermitRepository.findAllByUserId(userNo);
            List<Integer> roleIds = QaSystemUtils.getUserRoleIds(userPermit);
            userRoleDto userRoleDto = new userRoleDto();
            userRoleDto.setUserName(user.getUsername());
            userRoleDto.setRoleIds(roleIds);
            result = httpUtils.buildSuccess(Method.GET,userRoleDto);
        }catch (QuestionException qe){
            result = httpUtils.buildFailure(qe.getMessage(),Method.GET);
        }catch (Exception e) {
            e.printStackTrace();
            result = httpUtils.build500();
        }
        return result;
    }

    /**
     * 当method为 "reset_pwd" 时，为重置密码
     * @param userId  用户编号
     * @return   JSON格式
     */
    @GetMapping(value = {"userId"},params = "_method")
    public JsonResult updateUserPwd(@PathVariable("userId") Integer userId){
        JsonResult result = new JsonResult();
        String method = httpUtils.getParam("_method");
        User updateUserPwd = new User();
        updateUserPwd.setId(userId);
        if (method.equals(RESET_PWD)){
            updateUserPwd.setPassword(encoder.encode(DEFAULT_PASSWORD));
            userService.insert(updateUserPwd);
            result = httpUtils.buildSuccess(Method.GET,"重置密码成功");
        }
        return result;
    }


}
